package utils

import chisel3._
import chisel3.experimental.DataMirror
import chisel3.util._

import scala.math.log10

object Utils {
  def ScalaEnum(n: Int) = List.range(0, n)

  def log2 = (x: Double) => log10(x) / log10(2.0)

  def counter(cnt: UInt, maxCnt: Int) = {
    if (maxCnt == 0) {
      cnt := 0.U
    } else {
      when(cnt === (maxCnt - 1).U) {
        cnt := 0.U
      }.otherwise {
        cnt := cnt + 1.U
      }
    }
  }

  def counter(cnt: UInt, maxCnt: UInt) = {
    when(cnt === maxCnt - 1.U) {
      cnt := 0.U
    }.otherwise {
      cnt := cnt + 1.U
    }
  }

  def repeated[A](f: A => A, n: Int): A => A =
    (0 until n).foldLeft(identity[A] _)((ff, _) => ff.andThen(f))

  def sliceUIntToBytes(data: UInt): Seq[UInt] = {
    assert(data.getWidth >= 8, "sliceUIntToBytes needs an UInt which width >= 8")
    logger.debug(s"sliceUIntToBytes(${data.getWidth})")
    for (i <- 0 until data.getWidth / 8) yield {
      logger.debug(s"\t data(${i * 8 + 8}, ${i * 8})")
      data(i * 8 + 7, i * 8)
    }
  }

  def sliceUIntToWords(data: UInt): Seq[UInt] = {
    assert(data.getWidth >= 32, "sliceUIntToBytes needs an UInt which width >= 32")
    logger.debug(s"sliceUIntToWords(${data.getWidth})")
    for (i <- 0 until data.getWidth / 32) yield {
      logger.debug(s"\t data(${i * 32 + 32}, ${i * 32})")
      data(i * 32 + 31, i * 32)
    }
  }

  def UIntToMask(data: UInt, width: Int): UInt = (~((-1).S(width.W).asUInt << data.asTypeOf(UInt(6.W)))).asTypeOf(UInt(width.W))

  def magicWord = "xE7881CD0".U(32.W)

  def getOptionalPort[S <: Data](source: Option[S]) = if (source.nonEmpty) Some(source.get.cloneType) else None

  def setOptionalPort[S <: Data](source: Option[S], target: S) = if (source.nonEmpty) source.get := target

  def setOptionalPort[S <: Data](source: Option[S], target: Option[S]) = if (source.nonEmpty && target.nonEmpty) source.get := target.get

  @Deprecated
  def connectOptionalPort[S <: Data](source: Option[S], target: Option[S]) = {
    if (source.nonEmpty && target.nonEmpty) source.get <> target.get
    else if (source.nonEmpty) source.get <> 0.U
    else if (target.nonEmpty) target.get <> 0.U
  }

  def EnumOH(n: Int): List[Int] = (0 until n).map(i => 1 << i).toList

  def EnumOHUInt(n: Int) = EnumOH(n).map(_.U(n.W))

  def updateVec(source: Vec[UInt], index: UInt, value: UInt): Vec[UInt] = {
    val newVec = WireInit(source)
    newVec(index) := value
    newVec
  }
}
